#ifndef __aodvrtt_h__
#define __aodvrtt_h__

#include <map>

#include <cmu-trace.h>
#include <priqueue.h>
#include "aodvrtt_packet.h"
#include "aodvrtt_rtable.h"
#include "aodvrtt_rqueue.h"
#include <classifier/classifier-port.h>

/*
 Allows local repair of routes
 */
#define AODVRTT_LOCAL_REPAIR

/*
 Allows AODVRTT to use link-layer (802.11) feedback in determining when
 links are up/down.
 */
#define AODVRTT_LINK_LAYER_DETECTION

/*
 Causes AODVRTT to apply a "smoothing" function to the link layer feedback
 that is generated by 802.11.  In essence, it requires that RT_MAX_ERROR
 errors occurs within a window of RT_MAX_ERROR_TIME before the link
 is considered bad.
 */
#define AODVRTT_USE_LL_METRIC

/*
 Only applies if AODVRTT_USE_LL_METRIC is defined.
 Causes AODVRTT to apply omniscient knowledge to the feedback received
 from 802.11.  This may be flawed, because it does not account for
 congestion.
 */
//#define AODVRTT_USE_GOD_FEEDBACK


class AODVRTT;

#define MY_ROUTE_TIMEOUT        10                      	// 100 seconds
#define ACTIVE_ROUTE_TIMEOUT    10				// 50 seconds
#define REV_ROUTE_LIFE          6				// 5  seconds
#define BCAST_ID_SAVE           6				// 3 seconds
// No. of times to do network-wide search before timing out for 
// MAX_RREQ_TIMEOUT sec. 
#define RREQ_RETRIES            3  
// timeout after doing network-wide search RREQ_RETRIES times
#define MAX_RREQ_TIMEOUT	10.0 //sec
/* Various constants used for the expanding ring search */
#define TTL_START     5
#define TTL_THRESHOLD 7
#define TTL_INCREMENT 2 

// This should be somewhat related to arp timeout
#define NODE_TRAVERSAL_TIME     0.03             // 30 ms
#define LOCAL_REPAIR_WAIT_TIME  0.15 //sec
// Should be set by the user using best guess (conservative) 
#define NETWORK_DIAMETER        30             // 30 hops
// Must be larger than the time difference between a node propagates a route 
// request and gets the route reply back.

//#define RREP_WAIT_TIME     (3 * NODE_TRAVERSAL_TIME * NETWORK_DIAMETER) // ms
//#define RREP_WAIT_TIME     (2 * REV_ROUTE_LIFE)  // seconds
#define RREP_WAIT_TIME         1.0  // sec
#define ID_NOT_FOUND    0x00
#define ID_FOUND        0x01
//#define INFINITY        0xff

// The followings are used for the forward() function. Controls pacing.
#define DELAY 1.0           // random delay
#define NO_DELAY -1.0       // no delay 
// think it should be 30 ms
#define ARP_DELAY 0.01      // fixed delay to keep arp happy
#define HELLO_INTERVAL          1               // 1000 ms
#define ALLOWED_HELLO_LOSS      3               // packets
#define BAD_LINK_LIFETIME       3               // 3000 ms
#define MAXHELLOINTERVAL        (1.25 * HELLO_INTERVAL)
#define MINHELLOINTERVAL        (0.75 * HELLO_INTERVAL)
#define RTT_PROBE_INTERVAL      0.5              // 500 ms
#define MAX_RTT_PROBE_INTERVAL  (1.05 * RTT_PROBE_INTERVAL)
#define MIN_RTT_PROBE_INTERVAL  (0.95 * RTT_PROBE_INTERVAL)

/*
 Timers (Broadcast ID, Hello, Neighbor Cache, Route Cache)
 */
class BroadcastTimer: public Handler {
public:
    BroadcastTimer(AODVRTT* a) :
        agent(a) {
    }
    void handle(Event*);
private:
    AODVRTT *agent;
    Event intr;
};

class HelloTimer: public Handler {
public:
    HelloTimer(AODVRTT* a) :
        agent(a) {
    }
    void handle(Event*);
private:
    AODVRTT *agent;
    Event intr;
};

class NeighborTimer: public Handler {
public:
    NeighborTimer(AODVRTT* a) :
        agent(a) {
    }
    void handle(Event*);
private:
    AODVRTT *agent;
    Event intr;
};

class RouteCacheTimer: public Handler {
public:
    RouteCacheTimer(AODVRTT* a) :
        agent(a) {
    }
    void handle(Event*);
private:
    AODVRTT *agent;
    Event intr;
};

class LocalRepairTimer: public Handler {
public:
    LocalRepairTimer(AODVRTT* a) :
        agent(a) {
    }
    void handle(Event*);
private:
    AODVRTT *agent;
    Event intr;
};

class RttProbeTimer: public Handler {
public:
    RttProbeTimer(AODVRTT* a) :
        agent(a) {
    }
    void handle(Event*);
private:
    AODVRTT *agent;
    Event intr;
};

/*
 * Broadcast ID Cache
 */
class BroadcastID {
    friend class AODVRTT;
public:
    BroadcastID(nsaddr_t i, u_int32_t b) {
        src = i;
        id = b;
    }
protected:
    LIST_ENTRY(BroadcastID) link;
    nsaddr_t src;
    u_int32_t id;
    double expire; // now + BCAST_ID_SAVE s
};

LIST_HEAD(aodvrtt_bcache, BroadcastID);

/*
 The Routing Agent
 */
class AODVRTT: public Agent {

    /*
     * Make some friends first
     */
    friend class aodvrtt_rt_entry;
    friend class BroadcastTimer;
    friend class HelloTimer;
    friend class NeighborTimer;
    friend class RouteCacheTimer;
    friend class LocalRepairTimer;
    friend class RttProbeTimer;

public:
    AODVRTT(nsaddr_t id);

    void recv(Packet *p, Handler *);

protected:
    int command(int, const char * const *);
    int initialized() {
        return 1 && target_;
    }

    /*
     * Route Table Management
     */
    void rt_resolve(Packet *p);
    void rt_update(aodvrtt_rt_entry *rt, u_int32_t seqnum, u_int16_t hop_count, double metric, nsaddr_t nexthop,
            double expire_time);
    void rt_down(aodvrtt_rt_entry *rt);
    void local_rt_repair(aodvrtt_rt_entry *rt, Packet *p);
public:
    void rt_ll_failed(Packet *p);
    void handle_link_failure(nsaddr_t id);
protected:
    void rt_purge(void);

    void enque(aodvrtt_rt_entry *rt, Packet *p);
    Packet* deque(aodvrtt_rt_entry *rt);

    /*
     * Neighbour Management
     */
    void nb_insert(nsaddr_t id);
    AODVRTT_Neighbor* nb_lookup(nsaddr_t id);
    void nb_delete(nsaddr_t id);
    void nb_purge(void);

    /*
     * Broadcast ID Management
     */
    void id_insert(nsaddr_t id, u_int32_t bid);
    bool id_lookup(nsaddr_t id, u_int32_t bid);
    void id_purge(void);

    /*
     * Packet TX Routines
     */
    void forward(aodvrtt_rt_entry *rt, Packet *p, double delay);
    void sendHello(void);
    void sendRequest(nsaddr_t dst);
    void sendReply(nsaddr_t ipdst, u_int32_t hop_count, double metric, nsaddr_t rpdst, u_int32_t rpseq,
            u_int32_t lifetime, double timestamp);
    void sendError(Packet *p, bool jitter = true);

    /*
     * Packet RX Routines
     */
    void recvAODVRTT(Packet *p);
    void recvHello(Packet *p);
    void recvRequest(Packet *p);
    void recvReply(Packet *p);
    void recvError(Packet *p);

    /*
     * RTT support
     */
    void sendRttProbe();
    void sendRttProbeAck(nsaddr_t dst, double timestamp);
    void recvRttProbe(Packet* p);
    void recvRttProbeAck(Packet* p);
    map<nsaddr_t, double> neighbourRtts;

    /*
     * History management
     */
    double PerHopTime(aodvrtt_rt_entry *rt);

    nsaddr_t index; // IP Address of this node
    u_int32_t seqno; // Sequence Number
    int bid; // Broadcast ID

    aodvrtt_rtable rthead; // routing table
    aodvrtt_ncache nbhead; // Neighbor Cache
    aodvrtt_bcache bihead; // Broadcast ID Cache

    /*
     * Timers
     */
    BroadcastTimer btimer;
    HelloTimer htimer;
    NeighborTimer ntimer;
    RouteCacheTimer rtimer;
    LocalRepairTimer lrtimer;
    //    RttProbeTimer rptimer;

    /*
     * Routing Table
     */
    aodvrtt_rtable rtable;
    /*
     *  A "drop-front" queue used by the routing layer to buffer
     *  packets to which it does not have a route.
     */
    aodvrtt_rqueue rqueue;

    /*
     * A mechanism for logging the contents of the routing
     * table.
     */
    Trace *logtarget;

    /*
     * A pointer to the network interface queue that sits
     * between the "classifier" and the "link layer".
     */
    PriQueue *ifqueue;

    /*
     * Logging stuff
     */
    void log_link_del(nsaddr_t dst);
    void log_link_broke(Packet *p);
    void log_link_kept(nsaddr_t dst);

    /* for passing packets up to agents */
    PortClassifier *dmux_;
};

#endif /* __aodvrtt_h__ */
